package cn.edu.zstu.qingzhu.qingzhushop.provider;

import cn.ucloud.ufile.UfileClient;
import cn.ucloud.ufile.api.ApiError;
import cn.ucloud.ufile.api.object.ObjectConfig;
import cn.ucloud.ufile.api.object.policy.PolicyParam;
import cn.ucloud.ufile.api.object.policy.PutPolicy;
import cn.ucloud.ufile.api.object.policy.PutPolicyForCallback;
import cn.ucloud.ufile.auth.BucketAuthorization;
import cn.ucloud.ufile.auth.UfileObjectLocalAuthorization;
import cn.ucloud.ufile.auth.UfileObjectRemoteAuthorization;
import cn.ucloud.ufile.bean.PutObjectResultBean;
import cn.ucloud.ufile.bean.UfileErrorBean;
import cn.ucloud.ufile.exception.UfileClientException;
import cn.ucloud.ufile.exception.UfileServerException;
import cn.ucloud.ufile.http.HttpClient;
import cn.ucloud.ufile.http.OnProgressListener;
import cn.ucloud.ufile.http.UfileCallback;
import cn.ucloud.ufile.util.StorageType;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Author ShenTuZhiGang
 * @Version 1.0.0
 * @Date 2020-04-16 20:55
 */
@Service
@Slf4j
public class UCloudProvider {

    static {
        /**
         * 配置UfileClient，必须在使用UfileClient之前调用
         */
        UfileClient.configure(new UfileClient.Config(
                new HttpClient.Config(10, 5, TimeUnit.MINUTES)
                        .setTimeout(10 * 1000, 30 * 1000, 30 * 1000)
                        .setExecutorService(Executors.newSingleThreadExecutor())));
    }

    @Value("${ucloud.ufile.public-key}")
    private String publicKey;
    @Value("${ucloud.ufile.private-key}")
    private String privateKey;
    @Value("${ucloud.ufile.bucket-name}")
    private String bucketName;
    @Value("${ucloud.ufile.region}")
    private String region;
    @Value("${ucloud.ufile.proxy-suffix}")
    private String proxySuffix;
    @Value("${ucloud.ufile.expires}")
    private Integer expires;
    @Value("${ucloud.ufile.log.tag}")
    private String tag;
    @Autowired
    private BucketAuthorization bucketAuthorization;

    @Autowired
    private UfileObjectLocalAuthorization objectAuthorization;

    @Autowired
    private UfileObjectRemoteAuthorization objectRemoteAuthorization;

    @Autowired
    private ObjectConfig config;

    public String putFile(File file, String mimeType, String nameAs) {
        return putFile(file, mimeType, nameAs, bucketName);
    }

    public String putFile(File file, String mimeType, String nameAs, String toBucket) {
        try {
            /**
             * 上传回调策略
             * 必须填写回调接口url(目前仅支持http，不支持https)，可选填回调参数，回调参数请自行决定是否需要urlencode
             * 若配置上传回调，则上传接口的回调将会透传回调接口的response，包括httpCode
             */
            PutPolicy putPolicy = new PutPolicyForCallback.Builder("http://xxx.xxx.xxx.xxx[:port][/path]")
                    .addCallbackBody(new PolicyParam("key", "value"))
                    .build();
            PutObjectResultBean response = UfileClient.object(objectAuthorization, config)
                    .putObject(file, mimeType)
                    .nameAs(nameAs)
                    .toBucket(toBucket)
                    /**
                     * 配置文件存储类型，分别是标准、低频、冷存，对应有效值：STANDARD | IA | ARCHIVE
                     */
                    .withStorageType(StorageType.STANDARD)
                    /**
                     * 为云端对象配置自定义数据，每次调用将会替换之前数据。
                     * 所有的自定义数据总大小不能超过 8KB。
                     */
//                    .withMetaDatas()
                    /**
                     * 为云端对象添加自定义数据，可直接调用，无须先调用withMetaDatas
                     * key不能为空或者""
                     *
                     */
//                    .addMetaData(new Parameter<>("key","value"))
                    /**
                     * 配置上传回调策略
                     */
//                .withPutPolicy(putPolicy)
                    /**
                     * 是否上传校验MD5
                     */
//                .withVerifyMd5(false)
                    /**
                     * 指定progress callback的间隔
                     */
//                .withProgressConfig(ProgressConfig.callbackWithPercent(10))
                    /**
                     * 配置读写流Buffer的大小, Default = 256 KB, MIN = 4 KB, MAX = 4 MB
                     */
//                    .setBufferSize(4 << 20)
                    /**
                     * 配置进度监听
                     */
                    .setOnProgressListener(new OnProgressListener() {
                        @Override
                        public void onProgress(long bytesWritten, long contentLength) {
                            log.debug(tag, String.format("[progress] = %d%% - [%d/%d]", (int) (bytesWritten * 1.f / contentLength * 100), bytesWritten, contentLength));
                        }
                    })
                    .execute();
            log.debug(tag, String.format("[res] = %s", (response == null ? "null" : response.toString())));
            if (response != null && response.getRetCode() == 0) {
                String url = UfileClient.object(objectAuthorization, config)
                        .getDownloadUrlFromPrivateBucket(nameAs, toBucket, expires)
                        .createUrl();
                return url;
            } else {
                log.error("upload error,{}", response);
                throw new RuntimeException("File Upload Error");
            }
        } catch (UfileClientException e) {
            log.error("upload error,{}", nameAs, e);
            e.printStackTrace();
            throw new RuntimeException("File Upload Error");
        } catch (UfileServerException e) {
            log.error("upload error,{}", nameAs, e);
            e.printStackTrace();
            throw new RuntimeException("File Upload Error");
        }
    }

    public void putFileAsync(File file, String mimeType, String nameAs, String toBucket) throws UfileClientException {
        /**
         * 上传回调策略
         * 必须填写回调接口url(目前仅支持http，不支持https)，可选填回调参数，回调参数请自行决定是否需要urlencode
         * 若配置上传回调，则上传接口的回调将会透传回调接口的response，包括httpCode
         */
        PutPolicy putPolicy = new PutPolicyForCallback.Builder("http://xxx.xxx.xxx.xxx[:port][/path]")
                .addCallbackBody(new PolicyParam("key", "value"))
                .build();
        UfileClient.object(objectAuthorization, config)
                .putObject(file, mimeType)
                .nameAs(nameAs)
                .toBucket(toBucket)
                /**
                 * 配置文件存储类型，分别是标准、低频、冷存，对应有效值：STANDARD | IA | ARCHIVE
                 */
                .withStorageType(StorageType.STANDARD)
                /**
                 * 为云端对象配置自定义数据，每次调用将会替换之前数据。
                 * 所有的自定义数据总大小不能超过 8KB。
                 */
//                    .withMetaDatas()
                /**
                 * 为云端对象添加自定义数据，可直接调用，无须先调用withMetaDatas
                 * key不能为空或者""
                 *
                 */
//                    .addMetaData(new Parameter<>("key","value"))
                /**
                 * 配置上传回调策略
                 */
//                .withPutPolicy(putPolicy)
                /**
                 * 是否上传校验MD5
                 */
//                .withVerifyMd5(false)
                /**
                 * 指定progress callback的间隔
                 */
//                .withProgressConfig(ProgressConfig.callbackWithPercent(10))
                /**
                 * 配置读写流Buffer的大小, Default = 256 KB, MIN = 4 KB, MAX = 4 MB
                 */
//                .setBufferSize(4 << 20)
                .executeAsync(new UfileCallback<PutObjectResultBean>() {
                    @Override
                    public void onProgress(long bytesWritten, long contentLength) {
                        log.debug(tag, String.format("[progress] = %d%% - [%d/%d]", (int) (bytesWritten * 1.f / contentLength * 100), bytesWritten, contentLength));
                    }

                    @Override
                    public void onResponse(PutObjectResultBean response) {
                        log.debug(tag, String.format("[res] = %s", (response == null ? "null" : response.toString())));
                    }

                    @Override
                    public void onError(Request request, ApiError error, UfileErrorBean response) {
                        log.debug(tag, String.format("[error] = %s\n[info] = %s",
                                (error == null ? "null" : error.toString()),
                                (response == null ? "null" : response.toString())));
                    }
                });
    }

    public String putStream(InputStream stream, String mimeType, String nameAs) {
        return putStream(stream, mimeType, nameAs, bucketName);
    }

    public String putStream(InputStream stream, String mimeType, String nameAs, String toBucket) {
        try {
            /**
             * 上传回调策略
             * 必须填写回调接口url(目前仅支持http，不支持https)，可选填回调参数，回调参数请自行决定是否需要urlencode
             * 若配置上传回调，则上传接口的回调将会透传回调接口的response，包括httpCode
             */
            PutPolicy putPolicy = new PutPolicyForCallback.Builder("http://xxx.xxx.xxx.xxx[:port][/path]")
                    .addCallbackBody(new PolicyParam("key", "value"))
                    .build();
            PutObjectResultBean response = UfileClient.object(objectAuthorization, config)
                    .putObject(stream, mimeType)
                    .nameAs(nameAs)
                    .toBucket(toBucket)
                    /**
                     * 配置文件存储类型，分别是标准、低频、冷存，对应有效值：STANDARD | IA | ARCHIVE
                     */
                    .withStorageType(StorageType.STANDARD)
                    /**
                     * 为云端对象配置自定义数据，每次调用将会替换之前数据。
                     * 所有的自定义数据总大小不能超过 8KB。
                     */
//                    .withMetaDatas()
                    /**
                     * 为云端对象添加自定义数据，可直接调用，无须先调用withMetaDatas
                     * key不能为空或者""
                     *
                     */
//                    .addMetaData(new Parameter<>("key","value"))
                    /**
                     * 配置上传回调策略
                     */
//                .withPutPolicy(putPolicy)
                    /**
                     * 是否上传校验MD5
                     */
//                .withVerifyMd5(false)
                    /**
                     * 指定progress callback的间隔
                     */
//                .withProgressConfig(ProgressConfig.callbackWithPercent(10))
                    /**
                     * 配置读写流Buffer的大小, Default = 256 KB, MIN = 4 KB, MAX = 4 MB
                     */
//                    .setBufferSize(4 << 20)
                    /**
                     * 配置进度监听
                     */
                    .setOnProgressListener(new OnProgressListener() {
                        @Override
                        public void onProgress(long bytesWritten, long contentLength) {
                            log.debug(tag, String.format("[progress] = %d%% - [%d/%d]", (int) (bytesWritten * 1.f / contentLength * 100), bytesWritten, contentLength));
                        }
                    })
                    .execute();
            log.debug(tag, String.format("[res] = %s", (response == null ? "null" : response.toString())));
            if (response != null && response.getRetCode() == 0) {
                String url = UfileClient.object(objectAuthorization, config)
                        .getDownloadUrlFromPrivateBucket(nameAs, toBucket, expires)
                        .createUrl();
                return url;
            } else {
                log.error("upload error,{}", response);
                throw new RuntimeException("File Upload Error");
            }
        } catch (UfileClientException e) {
            log.error("upload error,{}", nameAs, e);
            e.printStackTrace();
            throw new RuntimeException("File Upload Error");
        } catch (UfileServerException e) {
            log.error("upload error,{}", nameAs, e);
            e.printStackTrace();
            throw new RuntimeException("File Upload Error");
        }
    }

    public void putStreamAsync(InputStream stream, String mimeType, String nameAs, String toBucket) throws UfileClientException {
        /**
         * 上传回调策略
         * 必须填写回调接口url(目前仅支持http，不支持https)，可选填回调参数，回调参数请自行决定是否需要urlencode
         * 若配置上传回调，则上传接口的回调将会透传回调接口的response，包括httpCode
         */
        PutPolicy putPolicy = new PutPolicyForCallback.Builder("http://xxx.xxx.xxx.xxx[:port][/path]")
                .addCallbackBody(new PolicyParam("key", "value"))
                .build();
        UfileClient.object(objectAuthorization, config)
                .putObject(stream, mimeType)
                .nameAs(nameAs)
                .toBucket(toBucket)
                /**
                 * 配置文件存储类型，分别是标准、低频、冷存，对应有效值：STANDARD | IA | ARCHIVE
                 */
                .withStorageType(StorageType.STANDARD)
                /**
                 * 为云端对象配置自定义数据，每次调用将会替换之前数据。
                 * 所有的自定义数据总大小不能超过 8KB。
                 */
//                    .withMetaDatas()
                /**
                 * 为云端对象添加自定义数据，可直接调用，无须先调用withMetaDatas
                 * key不能为空或者""
                 *
                 */
//                    .addMetaData(new Parameter<>("key","value"))
                /**
                 * 配置上传回调策略
                 */
//                .withPutPolicy(putPolicy)
                /**
                 * 是否上传校验MD5
                 */
//                .withVerifyMd5(false)
                /**
                 * 指定progress callback的间隔
                 */
//                .withProgressConfig(ProgressConfig.callbackWithPercent(10))
                /**
                 * 配置读写流Buffer的大小, Default = 256 KB, MIN = 4 KB, MAX = 4 MB
                 */
//                .setBufferSize(4 << 20)
                .executeAsync(new UfileCallback<PutObjectResultBean>() {
                    @Override
                    public void onProgress(long bytesWritten, long contentLength) {
                        log.debug(tag, String.format("[progress] = %d%% - [%d/%d]", (int) (bytesWritten * 1.f / contentLength * 100), bytesWritten, contentLength));
                    }

                    @Override
                    public void onResponse(PutObjectResultBean response) {
                        log.debug(tag, String.format("[res] = %s", (response == null ? "null" : response.toString())));
                    }

                    @Override
                    public void onError(Request request, ApiError error, UfileErrorBean response) {
                        log.debug(tag, String.format("[error] = %s\n[info] = %s",
                                (error == null ? "null" : error.toString()),
                                (response == null ? "null" : response.toString())));
                    }
                });
    }
}
